package main;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import validate.PasswordHasher;

import mistic.storage.Registro;
import mistic.storage.Storage;


/**
 *
 * @author MISTIC 
 */


public class OwnLoginModule implements LoginModule{
    private Subject subject;
    private CallbackHandler callbackHandler;
    private Map sharedState;
    private Map options;
    private Map<String,String> login_pass = new HashMap<String,String>();
    private Registro reg;

    @Override
	public void initialize (Subject subject, CallbackHandler handler,Map<java.lang.String, ?> sharedState, Map<java.lang.String, ?> options){
        this.subject = subject;
        this.callbackHandler = handler;
        this.sharedState = sharedState;
        this.options = options;
    }

    @Override
	public boolean login() throws LoginException {

        if (callbackHandler == null){
            throw new LoginException("no handler");
        }

        try {
            //Define and execute the callback handle
            NameCallback nameCb = new NameCallback("user: ");
            PasswordCallback passwdCb = new PasswordCallback("password: ", true);
            Callback[] cbs = new Callback[]{nameCb, passwdCb};
            this.callbackHandler.handle(cbs);

            //The user is authenticated using their credentials
		    if(!verifyCredentials(nameCb.getName(), new String(passwdCb.getPassword()))){
		    	return false;
		    }else{
		    	//The user authentication has been successful. The credentials are stored in the user's "principals". 
		    	obtainPrinciapls(nameCb.getName());
		    }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (UnsupportedCallbackException e) {
            e.printStackTrace();
        }
        throw new FailedLoginException();
    }

    @Override
	public boolean commit() throws LoginException {
        //You can modify this method. 
    	//The commit returns true.
        System.out.println("Commit true");
        return true;
    }

    @Override
	public boolean abort() throws LoginException {
        //This method is executed when "login" returns false.
        System.out.println("Abort: login has returned false");
        return true;
    }

    @Override
	public boolean logout() throws LoginException {
        return true;
    }

    private boolean verifyCredentials(String name, String password) {
		String usuarioValidar =   name;
		String claveValidar   =   password;

		/**
		 * La siguiente funcion dado un usuario obtiene el hash y la clave que antes se grabo
		 * y te devuelve una objeto, si el objeto es NULL significa que no lo encontro
		 */
		Registro reg = Storage.obtenerRegistro(usuarioValidar);
		
		if(reg == null){
			System.out.println("Registro no encontrado");
		}else{
			/**
			 * Antes de validar se debe verificar que no exceda los tres accesos
			 * entonces 
			 */
			if(!excedeLimite(reg.getFallos()) ){
				
				/**
				 * reg.getClaveHash() :  es el valor que se grabo en el archivo 
				 * reg.getSalt() :  es el valor que se grabo en el archivo 
				 * "clave" : VALOR 	QUE INGRESA EL USUARIO
				 */
				boolean isValid= PasswordHasher.validar(claveValidar, reg.getClaveHash(),reg.getSalt()); 
				
				System.out.println("La verificacion fue: " + isValid +" - para el usuario: " + reg.getUsuario());
				/**
				 * Para el caso en que es invalido la validacion incrementa el contador de fallos
				 */
				if(!isValid){
					Storage.incrementarFallo(reg.getUsuario());
				}
				this.reg =  reg;
				return isValid;
			}else{
				System.out.println("El usuario no fue validado por exceder los tres casos fallidos");
			}
		}
        return false;

    }

	private  static  boolean excedeLimite(int cantidadFallo){
		return (cantidadFallo>=3);
	}
    private boolean obtainPrinciapls (String nameCb){
    	this.subject.getPrincipals().add(new SimplePrincipal(nameCb,reg));
        return true;
    }

}
